/* level.c -- handles reading Lemmings levels (*.lvl)
 * Copyright (C) 2007  Brett McLarnon
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
 * USA.
 *
 * $Id: level.c 7 2007-12-09 06:34:40Z brett.mclarnon $
 */

#include "level.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <SDL/SDL_endian.h>

#define READ_MAX 64
#define MAX_OBJECTS 32
#define MAX_TERRAIN 400
#define MAX_STEEL 32

Level* level_load (FILE *stream) {
    Level* lvl = malloc(sizeof(Level));
    unsigned int bytesRead = 0;
    uint8_t buffer[READ_MAX];

    LvlObject object[MAX_OBJECTS];
    LvlTerrain terrain[MAX_TERRAIN];
    LvlSteel steel[MAX_STEEL];
    uint16_t modifier;

    int i;
    int count;

    /* globals */
    bytesRead += fread(buffer, 1, 8, stream);
    lvl->releaseRate   = SDL_SwapBE16(((uint16_t*)buffer)[0]);
    lvl->lemmingCount  = SDL_SwapBE16(((uint16_t*)buffer)[1]);
    lvl->lemmingRescue = SDL_SwapBE16(((uint16_t*)buffer)[2]);
    lvl->timeLimit     = SDL_SwapBE16(((uint16_t*)buffer)[3]);

    /* skill counts */
    bytesRead += fread(buffer, 1, 2*NUM_SKILLS, stream);
    lvl->skills[Climber] = SDL_SwapBE16(((uint16_t*)buffer)[0]);
    lvl->skills[Floater] = SDL_SwapBE16(((uint16_t*)buffer)[1]);
    lvl->skills[Bomber]  = SDL_SwapBE16(((uint16_t*)buffer)[2]);
    lvl->skills[Blocker] = SDL_SwapBE16(((uint16_t*)buffer)[3]);
    lvl->skills[Builder] = SDL_SwapBE16(((uint16_t*)buffer)[4]);
    lvl->skills[Basher]  = SDL_SwapBE16(((uint16_t*)buffer)[5]);
    lvl->skills[Miner]   = SDL_SwapBE16(((uint16_t*)buffer)[6]);
    lvl->skills[Digger]  = SDL_SwapBE16(((uint16_t*)buffer)[7]);

    /* more globals */
    bytesRead += fread(buffer, 1, 8, stream);
    lvl->screenStart = SDL_SwapBE16(((uint16_t*)buffer)[0]);
    lvl->tileset = SDL_SwapBE16(((uint16_t*)buffer)[1]);

    lvl->levelWidth = 0;

    /* Objects */
    count = 0;
    for (i = 0; i < MAX_OBJECTS; ++i) {
        bytesRead += fread(buffer, 1, 8, stream);
        object[count].xpos = SDL_SwapBE16(((int16_t*)buffer)[0]) - 16;
        object[count].ypos = SDL_SwapBE16(((int16_t*)buffer)[1]);
        object[count].id   = SDL_SwapBE16(((uint16_t*)buffer)[2]);
        object[count].modifier = SDL_SwapBE16(((uint16_t*)buffer)[3]);

        modifier = SDL_SwapBE16(((uint16_t*)buffer)[3]);
        object[count].modifier = None;
        object[count].modifier |= (modifier & 0x008F) ? Flip : None;
        object[count].modifier |= (modifier & 0x4000) ? OnlyTerrain : None;
        object[count].modifier |= (modifier & 0x8000) ? NoOverwrite : None;
        if (((uint32_t*)buffer)[0] != 0 || ((uint32_t*)buffer)[1] != 0)
            ++count;
    }
    lvl->objectCount = count;
    lvl->object = calloc(count, sizeof(LvlObject));
    memcpy(lvl->object, object, sizeof(LvlObject)*count);

    /* Terrain */
    count = 0;
    for (i = 0; i < MAX_TERRAIN; ++i) {
        bytesRead += fread(buffer, 1, 4, stream);
        modifier = buffer[0] >> 4;
        terrain[count].xpos = (SDL_SwapBE16(((int16_t*)buffer)[0]) & 0x0fff) - 16;
        terrain[count].ypos = (SDL_SwapBE16(((int16_t*)buffer)[1]) >> 7) - 4;
        terrain[count].id = buffer[3] & 0x00ff;

        terrain[count].modifier = None;
        terrain[count].modifier |= (modifier & 0x2) ? Remove : None;
        terrain[count].modifier |= (modifier & 0x4) ? Flip : None;
        terrain[count].modifier |= (modifier & 0x8) ? NoOverwrite : None;
        if (((uint32_t*)buffer)[0] != 0xffffffff)
            ++count;
    }
    lvl->terrainCount = count;
    lvl->terrain = calloc(count, sizeof(LvlTerrain));
    memcpy(lvl->terrain, terrain, sizeof(LvlTerrain)*count);
    
    /* Steel */
    count = 0;
    for (i = 0; i < MAX_STEEL; ++i) {
        bytesRead += fread(buffer, 1, 4, stream);
        steel[count].xpos = ((SDL_SwapBE16(((int16_t*)buffer)[0]) & 0xff80) >> 5) - 16;
        steel[count].ypos = (buffer[2] & 0x3f)*4;
        steel[count].width = (buffer[3] >> 4)*4 + 4;
        steel[count].height = (buffer[3] & 0x0f)*4 + 4;
        if (((uint32_t*)buffer)[0] != 0)
            ++count;
    }
    lvl->steelCount = count;
    lvl->steel = calloc(count, sizeof(LvlSteel));
    memcpy(lvl->steel, steel, sizeof(LvlSteel)*count);

    /* Level Name */
    bytesRead += fread(buffer, 1, 32, stream);

    /* 
     * find first character that's not a space and set it to null
     * (i.e., obtain a null-terminated string)
     */
    for (i = 31; i >= 0; --i)
        if (buffer[i] != ' ')
            break;
    buffer[i+1] = 0;

    /*
     * move forward through the buffer until we find a non-space
     * character
     */
    for (i = 0; i < 32; ++i)
        if (buffer[i] != ' ')
            break;

    /* malloc space for the level name */
    count = strnlen((char*)buffer+i, 33) + 1;
    lvl->name = calloc(count, sizeof(char));
    strncpy(lvl->name, (char*)(buffer+i), count);
    
    return lvl;
}

void level_free (Level* lvl) {
    if (lvl->name != NULL)
        free(lvl->name);
    free(lvl->object);
    free(lvl->terrain);
    free(lvl->steel);
    free(lvl);
}

void level_print (Level* lvl) {
    int i;

    printf("Level: %s\n", lvl->name);
    printf("  Release rate: %d [0x%04x]\n", lvl->releaseRate, lvl->releaseRate);
    printf("  Lemming Count: %d [0x%04x]\n", lvl->lemmingCount, lvl->lemmingCount);
    printf("  Lemming Rescue: %d [0x%04x]\n", lvl->lemmingRescue, lvl->lemmingRescue);
    printf("  Time limit: %d [0x%04x]\n", lvl->timeLimit, lvl->timeLimit);
    /* Skills */
    printf("  Screen start: %d [0x%04x]\n", lvl->screenStart, lvl->screenStart);
    printf("  Tileset: %d [0x%04x]\n", lvl->tileset, lvl->tileset);

    /* Object */
    printf("  Objects:\n");
    for (i = 0; i < lvl->objectCount; ++i) {
        printf("    (%d, %d) %x [%x]\n",
                lvl->object[i].xpos,
                lvl->object[i].ypos,
                lvl->object[i].id,
                lvl->object[i].modifier);
    }

    /* Terrain */
    printf("  Terrain:\n");
    for (i = 0; i < lvl->terrainCount; ++i) {
        printf("    (%d, %d) %x [%x]\n",
                lvl->terrain[i].xpos,
                lvl->terrain[i].ypos,
                lvl->terrain[i].id,
                lvl->terrain[i].modifier);
    }

    /* Steel */
    printf("  Steel:\n");
    for (i = 0; i < lvl->steelCount; ++i) {
        printf("    (%d, %d) %dx%d\n",
                lvl->steel[i].xpos,
                lvl->steel[i].ypos,
                lvl->steel[i].width,
                lvl->steel[i].height);
    }
}

int main(int argc, char** argv) {
    if (argc != 2)
        return EXIT_FAILURE;
    FILE* file = fopen(argv[1], "r");
    Level* lvl = level_load(file);

    level_print(lvl);
    level_free(lvl);
    return EXIT_SUCCESS;
}
